package com.kim.useroauth.server.service.impl;

import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kim.common.utilcom.constants.Oauth2Constants;
import com.kim.common.utilcom.exception.BusinessCommonException;
import com.kim.common.utilcom.model.Result;
import com.kim.common.utilcom.utils.BpwdEncoderUtil;
import com.kim.useroauth.common.dto.*;
import com.kim.useroauth.common.utils.UserUtil;
import com.kim.useroauth.server.client.OauthClient;
import com.kim.useroauth.server.client.UserInfoClient;
import com.kim.useroauth.server.constants.RefreshTokenType;
import com.kim.useroauth.server.constants.UserOauthServiceCode;
import com.kim.useroauth.server.dao.RoleMapper;
import com.kim.useroauth.server.dao.UserAccountMapper;
import com.kim.useroauth.server.dao.UserRoleMapper;
import com.kim.useroauth.server.entity.Role;
import com.kim.useroauth.server.entity.UserAccount;
import com.kim.useroauth.server.entity.UserRole;
import com.kim.useroauth.server.service.IUserOauthService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;

/**
 * 用户认证Service
 */
@Slf4j
@Service
public class UserOauthServiceImpl implements IUserOauthService {

    /**
     * 是否存在token(授权接口并且授权成功)
     */
    private static final String ACCESS_TOKEN = "access_token";

    /**
     * JSON中刷新令牌键{@value}
     */
    private static final String REFRESH_TOKEN_KEY = "refresh_token";

    /**
     * 认证服务远程调用Client
     */
    @Resource
    private OauthClient oauthClient;

    /**
     * 用户账户Mapper
     */
    @Resource
    private UserAccountMapper userAccountMapper;

    /**
     * 角色Mapper
     */
    @Resource
    private RoleMapper roleMapper;

    /**
     * 用户账户关联角色Mapper
     */
    @Resource
    private UserRoleMapper userRoleMapper;

    /**
     * 雪花算法
     */
    @Resource
    private Snowflake snowflake;

    /**
     * 用户信息远程调用Client
     */
    @Resource
    private UserInfoClient userInfoClient;


    /**
     * 调用认证服务密码模式授权认证
     * {@inheritDoc}
     */
    @Override
    public Result<Object> loginByUsernameAndPassword(String username,
                                                     String password,
                                                     String verificationCode,
                                                     String key,
                                                     String authType,
                                                     String clientId,
                                                     String clientSecret,
                                                     String scope) {
        // 验证码不为空为用户名密码验证码
        if (StringUtils.isNotBlank(verificationCode)) {
            log.info("密码模式（用户名密码验证码） authType -> {}, username -> {}, clientId -> {}, verificationCode -> {}, key -> {}, scope -> {} "
                    , authType, username, clientId, verificationCode, key, scope);
        } else {
            log.info("密码模式（用户名密码） authType -> {}, username -> {}, clientId -> {}, scope -> {} "
                    , authType, username, clientId, scope);
        }
        /**
         * 调用oauth进行认证
         */
        // 认证服务返回结果
        JSONObject result = oauthClient.getToken(
                Oauth2Constants.GRANT_MODE_PASSWORD,
                authType,
                username,
                password,
                verificationCode,
                key,
                clientId,
                clientSecret,
                scope);

        // 转换为Result对象
        return makeJsonToResult(result);
    }

    /**
     * 调用认证服务授权码模式获取授权码
     * {@inheritDoc}
     */
    @Override
    public void getAuthorizationCode(HttpServletResponse response, ReqGetAuthorizationCodeDTO reqGetAuthorizationCodeDto) throws IOException {
        // code 请求路径 后续使用网关路径
        String url = "http://localhost:18031/oauth/authorize?client_id=CLIENT_ID&response_type=code&redirect_uri=REDIRECT_URI";
        url = url.replace("CLIENT_ID", reqGetAuthorizationCodeDto.getClientId())
                .replace("REDIRECT_URI", reqGetAuthorizationCodeDto.getRedirectUri());

        // scope参数存在
        String scope = reqGetAuthorizationCodeDto.getScope();
        if (StringUtils.isNotBlank(scope)) {
            url = url.concat("&scope=").concat(scope);
        }
        // state参数存在
        String state = reqGetAuthorizationCodeDto.getState();
        if (StringUtils.isNotBlank(state)) {
            url = url.concat("&state=").concat(state);
        }

        log.info("获取code url -> {}", url);
        response.sendRedirect(url);
    }

    /**
     * 调用认证服务授权码模式授权认证
     * {@inheritDoc}
     */
    @Override
    public Result<Object> getTokenByAuthorizationCode(ReqGetTokenByAuthorizationCodeDTO reqGetTokenByAuthorizationCodeDto) {

        // 授权码
        String code = reqGetTokenByAuthorizationCodeDto.getCode();
        // 重定向URL
        String redirectUri = reqGetTokenByAuthorizationCodeDto.getRedirectUri();
        // 客户端ID
        String clientId = reqGetTokenByAuthorizationCodeDto.getClientId();
        // 客户端密钥
        String clientSecret = reqGetTokenByAuthorizationCodeDto.getClientSecret();


        log.info("授权码模式 code -> {}, redirect_uri -> {}, client_id -> {}", code, redirectUri, clientId);

        // 认证服务返回结果
        JSONObject result = oauthClient.getTokenByAuthorizationCode(
                Oauth2Constants.GRANT_MODE_AUTHORIZATION_CODE,
                code,
                redirectUri,
                clientId, clientSecret);

        // 转换为Result对象
        return makeJsonToResult(result);
    }

    /**
     * 调用认证服务刷新令牌
     * <p>刷新token类别  1,只返回刷新的token 2,返回刷新的token及refreshToken</p>
     * {@inheritDoc}
     */
    @Override
    public Result<Object> refreshToken(ReqRefreshTokenDTO reqRefreshTokenDTO) {
        // 认证服务返回结果
        JSONObject jsonObject = oauthClient.getTokenByRefreshToken(
                Oauth2Constants.GRANT_MODE_REFRESH_TOKEN,
                reqRefreshTokenDTO.getRefreshToken(),
                reqRefreshTokenDTO.getClientId(),
                reqRefreshTokenDTO.getClientSecret(),
                reqRefreshTokenDTO.getScope()
        );

        // 刷新token类型为只返回token
        if (RefreshTokenType.ONLY_RETURN_TOKEN.getType().equals(reqRefreshTokenDTO.getType())) {
            // 请求成功
            if (jsonObject.containsKey(REFRESH_TOKEN_KEY)) {
                Object o = jsonObject.remove(REFRESH_TOKEN_KEY);
                log.info(o.toString());
            }
        }
        // 转换为Result对象
        return makeJsonToResult(jsonObject);
    }

    /**
     * 调用认证服务密码模式(手机号 + 验证码)授权认证
     * {@inheritDoc}
     */
    @Override
    public Result<Object> loginByPhone(ReqPhoneLoginDTO reqPhoneLoginDTO) {

        // 客户端ID
        String clientId = reqPhoneLoginDTO.getClientId();
        // 客户端密钥
        String clientSecret = reqPhoneLoginDTO.getClientSecret();
        // 手机号
        String phoneNumber = reqPhoneLoginDTO.getPhoneNumber();
        // 短信验证码
        String phoneCode = reqPhoneLoginDTO.getPhoneCode();
        // 授权作用域
        String scope = reqPhoneLoginDTO.getScope();
        // 授权类型
        String authType = reqPhoneLoginDTO.getAuthType();

        log.info("密码模式（手机号验证码） authType -> {}, phoneNumber -> {},  clientId -> {}, scope -> {} "
                , authType, phoneNumber, clientId, scope);

        // 认证服务返回结果
        JSONObject result = oauthClient.getTokenByPhone(
                Oauth2Constants.GRANT_MODE_PASSWORD,
                authType,
                phoneNumber,
                phoneCode,
                clientId,
                clientSecret,
                scope);

        // 转换为Result对象
        return makeJsonToResult(result);
    }

    /**
     * 用户注册
     * todo 分布式事务
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> userRegister(String phoneCode, String phoneNumber, String password, String shareCode) {

        // todo 验证短信验证码是否有效
        //测试代码块，所以将验证码定为：1234
        if (!"1234".equals(phoneCode)) {
            return Result.failed(UserOauthServiceCode.SMS_CODE_BAD.getCode(), UserOauthServiceCode.SMS_CODE_BAD.getMsg());
        }

        // 查询电话号是否被使用
        QueryWrapper<UserAccount> wrapper = new QueryWrapper<>();
        wrapper.eq("phone", phoneNumber);
        UserAccount userAccount = userAccountMapper.selectOne(wrapper);
        // 判断电话号是否注册过
        if (null != userAccount) {
            return Result.failed(UserOauthServiceCode.PHONE_ALREADY_USE.getCode(), UserOauthServiceCode.PHONE_ALREADY_USE.getMsg());
        }

        // 初始化用户账户
        return addUserAccount(snowflake.nextId(), phoneNumber, password, shareCode, null);
    }

    /**
     * 调用认证服务密码模式(手机号)授权认证
     * {@inheritDoc}
     */
    @Override
    public Result<Object> getTokenByUserRegister(String phoneNumber, String clientId, String clientSecret, String scope) {


        log.info("密码模式（手机号） authType -> {}, phoneNumber -> {},  clientId -> {}, scope -> {} "
                , Oauth2Constants.GRANT_MODE_REGISTER, phoneNumber, clientId, scope);

        // 认证服务返回结果
        JSONObject result = oauthClient.getTokenByPhone(
                Oauth2Constants.GRANT_MODE_PASSWORD,
                Oauth2Constants.GRANT_MODE_REGISTER,
                phoneNumber,
                null,
                clientId,
                clientSecret,
                scope);

        // 转换为Result对象
        return makeJsonToResult(result);
    }

    /**
     * 调用认证服务密码模式(微信授权认证)
     * {@inheritDoc}
     */
    @Override
    public Result<Object> loginByWeChat(ReqWeChatLoginDTO reqWeChatLoginDTO) {

        // 客户端ID
        String clientId = reqWeChatLoginDTO.getClientId();
        // 客户端密钥
        String clientSecret = reqWeChatLoginDTO.getClientSecret();
        // 微信ID
        String unionId = reqWeChatLoginDTO.getUnionId();
        // 授权作用域
        String scope = reqWeChatLoginDTO.getScope();
        // 授权类型
        String authType = reqWeChatLoginDTO.getAuthType();

        log.info("密码模式（微信授权认证） authType -> {}, unionId -> {},  clientId -> {}, scope -> {} "
                , authType, unionId, clientId, scope);

        // 认证服务返回结果
        JSONObject result = oauthClient.getTokenByWeChat(
                Oauth2Constants.GRANT_MODE_PASSWORD,
                authType,
                unionId,
                clientId,
                clientSecret,
                scope);

        // 转换为Result对象
        return makeJsonToResult(result);
    }

    @Override
    public Result getTokenByCredentials(ReqGetTokenByCredentialsDTO reqGetTokenByCredentialsDTO) {
        // grant_type
        String grantType = reqGetTokenByCredentialsDTO.getGrantType();
        //clientId
        String clientId = reqGetTokenByCredentialsDTO.getClientId();
        //clientSecret
        String clientSecret = reqGetTokenByCredentialsDTO.getClientSecret();
        //scope
        String scope = reqGetTokenByCredentialsDTO.getScope();

        log.info("凭证模式登陆 grantType -> {}, clientId -> {},  clientSecret -> {}, scope -> {} "
                , grantType, clientId, clientSecret, scope);

        JSONObject result = oauthClient.getTokenByCredentials(
                Oauth2Constants.GRANT_MODE_CLIENT_CREDENTIALS,
                clientId,
                clientSecret,
                scope
        );

        //转换Result对象
        return makeJsonToResult(result);
    }

    /**
     * 初始化用户账户
     *
     * @param accountId                用户ID
     * @param phoneNumber              手机号
     * @param password                 密码
     * @param shareCode                分享码（可能为空）
     * @param reqUserInfoInitializeDTO 用户信息
     * @return org.surge.oauth2common.model.Result
     */
    private Result<Object> addUserAccount(long accountId, String phoneNumber, String password, String shareCode,
                                          ReqUserInfoInitializeDTO reqUserInfoInitializeDTO) {

        // 添加用户
        UserAccount userAccount = new UserAccount();
        // 用户账户ID
        userAccount.setId(accountId);
        // 魔咕号
        String username = "SURGE" + snowflake.nextId();
        userAccount.setUsername(username);
        // 手机号
        userAccount.setPhone(phoneNumber);
        // 不为空时设置密码
        if (StringUtils.isNotBlank(password)) {
            userAccount.setPassword(BpwdEncoderUtil.bCryptPassword(password));
        }
        // 注册时间
        userAccount.setCreateTime(LocalDateTime.now());
        // 新增用户
        userAccountMapper.insert(userAccount);
        // 用户账户ID
        log.info("新增用户id -> {}", accountId);

        // 添加用户角色
        //查询普通用户角色
        QueryWrapper<Role> wrapper = new QueryWrapper<>();
        wrapper.eq("value", "USER")
                .eq("organize", "SURGE");
        Role role = roleMapper.selectOne(wrapper);
        //新增用户角色
        UserRole userRole = new UserRole();
        userRole.setUserId(accountId);
        userRole.setRoleId(role.getId());
        userRoleMapper.insert(userRole);
        log.info("新增用户角色关系 用户id -> {}, 角色 -> {}", accountId, "USER");

        // 添加用户信息 (远程)
        if (null == reqUserInfoInitializeDTO) {
            reqUserInfoInitializeDTO = new ReqUserInfoInitializeDTO();
        }
        // 账户ID
        reqUserInfoInitializeDTO.setAccountId(accountId);
        // 昵称
        String nickname = reqUserInfoInitializeDTO.getNickname();
        if (StringUtils.isNotEmpty(nickname)) {
            reqUserInfoInitializeDTO.setNickname(nickname);
        }
        // 性别
        String sex = reqUserInfoInitializeDTO.getSex();
        reqUserInfoInitializeDTO.setSex(UserUtil.makeUserAccountSex(sex));
        // 头像
        String headPortrait = reqUserInfoInitializeDTO.getHeadPortrait();
        if (StringUtils.isNotEmpty(headPortrait)) {
            reqUserInfoInitializeDTO.setHeadPortrait(headPortrait);
        }
        // 初始化用户信息
        Result addUserInfo = userInfoClient.initializeUserInfo(reqUserInfoInitializeDTO);
        boolean success = addUserInfo.isSuccess();
        log.info("新增用户信息 -> {}", success);
        if (!success) {
            log.error("初始化用户信息失败-> {}", addUserInfo.toString());
            throw new BusinessCommonException(addUserInfo.getRespCode(), addUserInfo.getRespMsg());
        }
        return Result.succeed();
    }


    /**
     * 处理认证服务返回结果为系统返回结果Result
     *
     * @param result 认证服务返回结果
     * @return org.surge.oauth2common.model.Result
     */
    private Result<Object> makeJsonToResult(JSONObject result) {
        // 是否为token对象
        if (result.containsKey(ACCESS_TOKEN)) {
            return Result.succeed(result);
        }
        return result.toJavaObject(Result.class);
    }
}
